1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.codehaus.groovy.classgen.asm;
20
21 import groovy.lang.GroovyRuntimeException;
22
23 import java.lang.reflect.Constructor;
24 import java.util.Map;
25
26 import org.codehaus.groovy.GroovyBugError;
27 import org.codehaus.groovy.ast.ClassHelper;
28 import org.codehaus.groovy.ast.ClassNode;
29 import org.codehaus.groovy.ast.ConstructorNode;
30 import org.codehaus.groovy.ast.InnerClassNode;
31 import org.codehaus.groovy.ast.InterfaceHelperClassNode;
32 import org.codehaus.groovy.ast.MethodNode;
33 import org.codehaus.groovy.classgen.AsmClassGenerator;
34 import org.codehaus.groovy.classgen.GeneratorContext;
35 import org.codehaus.groovy.control.CompilerConfiguration;
36 import org.codehaus.groovy.control.SourceUnit;
37 import org.objectweb.asm.ClassVisitor;
38 import org.objectweb.asm.MethodVisitor;
39 import org.objectweb.asm.Opcodes;
40
41 public class WriterController {
42
43 private static Constructor indyWriter, indyCallSiteWriter, indyBinHelper;
44 static {
45 try {
46 ClassLoader cl = WriterController.class.getClassLoader();
47 Class indyClass = cl.loadClass("org.codehaus.groovy.classgen.asm.indy.InvokeDynamicWriter");
48 indyWriter = indyClass.getConstructor(WriterController.class);
49 indyClass = cl.loadClass("org.codehaus.groovy.classgen.asm.indy.IndyCallSiteWriter");
50 indyCallSiteWriter = indyClass.getConstructor(WriterController.class);
51 indyClass = cl.loadClass("org.codehaus.groovy.classgen.asm.indy.IndyBinHelper");
52 indyBinHelper = indyClass.getConstructor(WriterController.class);
53 } catch (Exception e) {
54 indyWriter = null;
55 indyCallSiteWriter = null;
56 indyBinHelper = null;
57 }
58 }
59 private AsmClassGenerator acg;
60 private MethodVisitor methodVisitor;
61 private CompileStack compileStack;
62 private OperandStack operandStack;
63 private ClassNode classNode;
64 private CallSiteWriter callSiteWriter;
65 private ClassVisitor cv;
66 private ClosureWriter closureWriter;
67 private String internalClassName;
68 private InvocationWriter invocationWriter;
69 private BinaryExpressionHelper binaryExpHelper, fastPathBinaryExpHelper;
70 private UnaryExpressionHelper unaryExpressionHelper, fastPathUnaryExpressionHelper;
71 private AssertionWriter assertionWriter;
72 private String internalBaseClassName;
73 private ClassNode outermostClass;
74 private MethodNode methodNode;
75 private SourceUnit sourceUnit;
76 private ConstructorNode constructorNode;
77 private GeneratorContext context;
78 private InterfaceHelperClassNode interfaceClassLoadingClass;
79 public boolean optimizeForInt = true;
80 private StatementWriter statementWriter;
81 private boolean fastPath = false;
82 private TypeChooser typeChooser;
83 private int bytecodeVersion = Opcodes.V1_5;
84 private int lineNumber = -1;
85 private int helperMethodIndex = 0;
86
87 public void init(AsmClassGenerator asmClassGenerator, GeneratorContext gcon, ClassVisitor cv, ClassNode cn) {
88 CompilerConfiguration config = cn.getCompileUnit().getConfig();
89 Map<String,Boolean> optOptions = config.getOptimizationOptions();
90 boolean invokedynamic=false;
91 if (optOptions.isEmpty()) {
92
93 } else if (Boolean.FALSE.equals(optOptions.get("all"))) {
94 optimizeForInt=false;
95
96 } else {
97 if (Boolean.TRUE.equals(optOptions.get("indy"))) invokedynamic=true;
98 if (Boolean.FALSE.equals(optOptions.get("int"))) optimizeForInt=false;
99 if (invokedynamic) optimizeForInt=false;
100
101 }
102 this.classNode = cn;
103 this.outermostClass = null;
104 this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
105
106 bytecodeVersion = chooseBytecodeVersion(invokedynamic, config.getTargetBytecode());
107
108 if (invokedynamic) {
109 try {
110 this.invocationWriter = (InvocationWriter) indyWriter.newInstance(this);
111 this.callSiteWriter = (CallSiteWriter) indyCallSiteWriter.newInstance(this);
112 this.binaryExpHelper = (BinaryExpressionHelper) indyBinHelper.newInstance(this);
113 } catch (Exception e) {
114 throw new GroovyRuntimeException("Cannot use invokedynamic, indy module was excluded from this build.");
115 }
116 } else {
117 this.callSiteWriter = new CallSiteWriter(this);
118 this.invocationWriter = new InvocationWriter(this);
119 this.binaryExpHelper = new BinaryExpressionHelper(this);
120 }
121
122 this.unaryExpressionHelper = new UnaryExpressionHelper(this);
123 if (optimizeForInt) {
124 this.fastPathBinaryExpHelper = new BinaryExpressionMultiTypeDispatcher(this);
125
126 this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this);
127 } else {
128 this.fastPathBinaryExpHelper = this.binaryExpHelper;
129 this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this);
130 }
131
132 this.operandStack = new OperandStack(this);
133 this.assertionWriter = new AssertionWriter(this);
134 this.closureWriter = new ClosureWriter(this);
135 this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
136 this.acg = asmClassGenerator;
137 this.sourceUnit = acg.getSourceUnit();
138 this.context = gcon;
139 this.compileStack = new CompileStack(this);
140 this.cv = cv;
141 if (optimizeForInt) {
142 this.statementWriter = new OptimizingStatementWriter(this);
143 } else {
144 this.statementWriter = new StatementWriter(this);
145 }
146 this.typeChooser = new StatementMetaTypeChooser();
147 }
148
149 private static int chooseBytecodeVersion(final boolean invokedynamic, final String targetBytecode) {
150 if (invokedynamic) {
151 if (CompilerConfiguration.JDK8.equals(targetBytecode)) {
152 return Opcodes.V1_8;
153 }
154 return Opcodes.V1_7;
155 } else {
156 if (CompilerConfiguration.JDK4.equals(targetBytecode)) {
157 return Opcodes.V1_4;
158 }
159 if (CompilerConfiguration.JDK5.equals(targetBytecode)) {
160 return Opcodes.V1_5;
161 }
162 if (CompilerConfiguration.JDK6.equals(targetBytecode)) {
163 return Opcodes.V1_6;
164 }
165 if (CompilerConfiguration.JDK7.equals(targetBytecode)) {
166 return Opcodes.V1_7;
167 }
168 if (CompilerConfiguration.JDK8.equals(targetBytecode)) {
169 return Opcodes.V1_8;
170 }
171 }
172 throw new GroovyBugError("Bytecode version ["+targetBytecode+"] is not supported by the compiler");
173 }
174
175 public AsmClassGenerator getAcg() {
176 return acg;
177 }
178
179 public void setMethodVisitor(MethodVisitor methodVisitor) {
180 this.methodVisitor = methodVisitor;
181 }
182
183 public MethodVisitor getMethodVisitor() {
184 return methodVisitor;
185 }
186
187 public CompileStack getCompileStack() {
188 return compileStack;
189 }
190
191 public OperandStack getOperandStack() {
192 return operandStack;
193 }
194
195 public ClassNode getClassNode() {
196 return classNode;
197 }
198
199 public CallSiteWriter getCallSiteWriter() {
200 return callSiteWriter;
201 }
202
203 public ClassVisitor getClassVisitor() {
204 return cv;
205 }
206
207 public ClosureWriter getClosureWriter() {
208 return closureWriter;
209 }
210
211 public ClassVisitor getCv() {
212 return cv;
213 }
214
215 public String getInternalClassName() {
216 return internalClassName;
217 }
218
219 public InvocationWriter getInvocationWriter() {
220 return invocationWriter;
221 }
222
223 public BinaryExpressionHelper getBinaryExpressionHelper() {
224 if (fastPath) {
225 return fastPathBinaryExpHelper;
226 } else {
227 return binaryExpHelper;
228 }
229 }
230
231 public UnaryExpressionHelper getUnaryExpressionHelper() {
232 if (fastPath) {
233 return fastPathUnaryExpressionHelper;
234 } else {
235 return unaryExpressionHelper;
236 }
237 }
238
239 public AssertionWriter getAssertionWriter() {
240 return assertionWriter;
241 }
242
243 public TypeChooser getTypeChooser() {
244 return typeChooser;
245 }
246
247 public String getInternalBaseClassName() {
248 return internalBaseClassName;
249 }
250
251 public MethodNode getMethodNode() {
252 return methodNode;
253 }
254
255 public void setMethodNode(MethodNode mn) {
256 methodNode = mn;
257 constructorNode = null;
258 }
259
260 public ConstructorNode getConstructorNode(){
261 return constructorNode;
262 }
263
264 public void setConstructorNode(ConstructorNode cn) {
265 constructorNode = cn;
266 methodNode = null;
267 }
268
269 public boolean isNotClinit() {
270 return methodNode == null || !methodNode.getName().equals("<clinit>");
271 }
272
273 public SourceUnit getSourceUnit() {
274 return sourceUnit;
275 }
276
277 public boolean isStaticContext() {
278 if (compileStack!=null && compileStack.getScope()!=null) {
279 return compileStack.getScope().isInStaticContext();
280 }
281 if (!isInClosure()) return false;
282 if (constructorNode != null) return false;
283 return classNode.isStaticClass() || methodNode.isStatic();
284 }
285
286 public boolean isInClosure() {
287 return classNode.getOuterClass() != null
288 && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
289 }
290
291 public boolean isInClosureConstructor() {
292 return constructorNode != null
293 && classNode.getOuterClass() != null
294 && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
295 }
296
297 public boolean isNotExplicitThisInClosure(boolean implicitThis) {
298 return implicitThis || !isInClosure();
299 }
300
301
302 public boolean isStaticMethod() {
303 return methodNode != null && methodNode.isStatic();
304 }
305
306 public ClassNode getReturnType() {
307 if (methodNode != null) {
308 return methodNode.getReturnType();
309 } else if (constructorNode != null) {
310 return constructorNode.getReturnType();
311 } else {
312 throw new GroovyBugError("I spotted a return that is neither in a method nor in a constructor... I can not handle that");
313 }
314 }
315
316 public boolean isStaticConstructor() {
317 return methodNode != null && methodNode.getName().equals("<clinit>");
318 }
319
320 public boolean isConstructor() {
321 return constructorNode!=null;
322 }
323
324
325
326
327
328 public boolean isInScriptBody() {
329 if (classNode.isScriptBody()) {
330 return true;
331 } else {
332 return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
333 }
334 }
335
336 public String getClassName() {
337 String className;
338 if (!classNode.isInterface() || interfaceClassLoadingClass == null) {
339 className = internalClassName;
340 } else {
341 className = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass);
342 }
343 return className;
344 }
345
346 public ClassNode getOutermostClass() {
347 if (outermostClass == null) {
348 outermostClass = classNode;
349 while (outermostClass instanceof InnerClassNode) {
350 outermostClass = outermostClass.getOuterClass();
351 }
352 }
353 return outermostClass;
354 }
355
356 public GeneratorContext getContext() {
357 return context;
358 }
359
360 public void setInterfaceClassLoadingClass(InterfaceHelperClassNode ihc) {
361 interfaceClassLoadingClass = ihc;
362 }
363
364 public InterfaceHelperClassNode getInterfaceClassLoadingClass() {
365 return interfaceClassLoadingClass;
366 }
367
368 public boolean shouldOptimizeForInt() {
369 return optimizeForInt;
370 }
371
372 public StatementWriter getStatementWriter() {
373 return statementWriter;
374 }
375
376 public void switchToFastPath() {
377 fastPath = true;
378 resetLineNumber();
379 }
380
381 public void switchToSlowPath() {
382 fastPath = false;
383 resetLineNumber();
384 }
385
386 public boolean isFastPath() {
387 return fastPath;
388 }
389
390 public int getBytecodeVersion() {
391 return bytecodeVersion;
392 }
393
394 public int getLineNumber() {
395 return lineNumber;
396 }
397
398 public void setLineNumber(int n) {
399 lineNumber = n;
400 }
401
402 public void resetLineNumber() {
403 setLineNumber(-1);
404 }
405
406 public int getNextHelperMethodIndex() {
407 return helperMethodIndex++;
408 }
409 }